package test.base.medium;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.ByteBuffer;

import org.bytedeco.ffmpeg.avcodec.AVCodec;
import org.bytedeco.ffmpeg.avcodec.AVCodecContext;
import org.bytedeco.ffmpeg.avcodec.AVPacket;
import static org.bytedeco.ffmpeg.global.avformat.*;
import static org.bytedeco.ffmpeg.global.avcodec.*;
import static org.bytedeco.ffmpeg.global.avutil.*;
import static org.bytedeco.ffmpeg.global.swscale.*;

import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.avformat.AVStream;
import org.bytedeco.ffmpeg.avutil.AVDictionary;
import org.bytedeco.ffmpeg.avutil.AVFrame;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.ffmpeg.swscale.SwsContext;
import org.bytedeco.javacpp.BytePointer;
import org.bytedeco.javacpp.DoublePointer;
import org.bytedeco.javacpp.Pointer;
import org.bytedeco.javacpp.PointerPointer;
import hyl.base.medium.ImageFrame;
import hyl.base.medium.MyUI;
import hyl.core.MyFun;

public class ffmpegtest2 {

	public static void main(String[] arg) {
		
		String rtspPath = "rtsp://admin:nt123321@192.168.0.2:554/h264/1/sub/av_stream";
		
		try {
			openVideo(rtspPath,640,480);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public static ByteBuffer saveFrame(AVFrame pFrame, int width, int height){
        BytePointer data = pFrame.data(0);
        int size = width * height * 3;
        ByteBuffer buf = data.position(0).limit(size).asBuffer();
        System.out.println("sav:"+buf);
       // System.out.println(buf.array());
        
        return buf;
    }
	public static BufferedImage convertimg(AVFrame pFrame, int width, int height) {
		ByteBuffer src=saveFrame(pFrame,  width,  height);
		 
   
		System.out.println(MyFun.join(src," width:",width,"height:",height));
		return MyUI.byteBuffer2BGR(src, width, height);
	}
	// , String out_file
	private static String openVideo(String url,int 宽,int 高) throws IOException {
		
		AVFormatContext pFormatCtx = new AVFormatContext(null);
		int i, videoStream;
		AVCodecContext pCodecCtx = null;
		AVCodec pCodec = null;
	
	//	int[] frameFinished = new int[1];
		AVDictionary optionsDict = null;
		//int 输入格式 = AV_PIX_FMT_YUVJ420P;
		int 输出格式 =AV_PIX_FMT_BGR24;
		AVFrame pFrameRGB = null;
		int numBytes;
		BytePointer buffer = null;
		SwsContext sws_ctx = null;
//ssl时才需要开启网络解析
		// avformat_network_init();
		// 读取视频文件
//主要就是根据url创建InputStream，并且会根据不同协议（rtmp/rtsp/hls/文件等）尝试读取一些文件头数据（流媒体头数据）。
		if (avformat_open_input(pFormatCtx, url, null, null) != 0) {
			return "无法打开"; // Couldn't open file

		}

		// 查找视频信息
		if (avformat_find_stream_info(pFormatCtx, (PointerPointer<Pointer>) null) < 0) {
			return "无法发现视频流信息";// Couldn't find stream information
		}
		/*
		 * 打印有关输入或输出格式的详细信息，例如<br> 持续时间，比特率，流，容器，程序，元数据，边数据，<br> 编解码器和时基。
		 */
		av_dump_format(pFormatCtx, 0, url, 0);// Dump information about file onto standard error

		// 发现关键帧
		videoStream = -1;
		AVStream stream = null;
		for (i = 0; i < pFormatCtx.nb_streams(); i++) {
			stream = pFormatCtx.streams(i);
			// stream.codecpar().codec_id();
			// stream.codecpar().codec_type()
			if (stream.codecpar().codec_type() == AVMEDIA_TYPE_VIDEO) {
				videoStream = i;
				break;
			}
		}
		if (videoStream == -1) {
			return "找不到关键帧";
		}

		// 从关键帧 获取视频编码
		// pCodecCtx = pFormatCtx.streams(videoStream).codec();

		// 查找解码器
		// pCodec = avcodec_find_decoder(pCodecCtx.codec_id());
		// 查找解码器
		pCodec = avcodec_find_decoder(stream.codecpar().codec_id());
		// 从解码器获取解码器对象
		pCodecCtx = avcodec_alloc_context3(pCodec);// 需要使用avcodec_free_context释放
		// 事实上codecpar包含了大部分解码器相关的信息，这里是直接从AVCodecParameters复制到AVCodecContext
		avcodec_parameters_to_context(pCodecCtx, stream.codecpar());
		// av_codec_set_pkt_timebase(pCodecCtx, stream->time_base);
		if (pCodec == null) {
			System.err.println("Unsupported codec!");
			return "缺少解码器";
		}
		// 打开解码器
		if (avcodec_open2(pCodecCtx, pCodec, optionsDict) < 0) {
			return "无法打开解码器";
		}
		
		AVFrame pFrame = null;

		/*
		 * 分配一个AVFrame并将其字段设置为默认值。结果<br> 必须使用av_frame_free（）释放结构。<br>
		 * 
		 * @返回一个用默认值填充的AVFrame或失败时为NULL。<br> 注意这只分配AVFrame本身，而不分配数据缓冲区。那些<br>
		 * 必须通过其他方式分配，例如使用av_frame_get_buffer（）或手动操作。<br>
		 */
		pFrame = av_frame_alloc();// 分配帧内存

		pFrameRGB = av_frame_alloc();
		if (pFrameRGB == null) {
			return "无法分配avframe";
		}
		int width = pCodecCtx.width(), height = pCodecCtx.height();
		pFrameRGB.width(width);
		pFrameRGB.height(height);
		pFrameRGB.format(输出格式);
		// 确定图像所需的缓冲区大小并分配缓冲区
		// numBytes = avpicture_get_size(图像格式, width, height);
		numBytes = av_image_get_buffer_size(输出格式, width, height, 1);
		System.out.println( "width:"+width);
		System.out.println( "height:"+height);
		System.out.println( "numBytes:"+numBytes);
		/*
		 * 分配并返回一个SwsContext。你需要它来表演
		 * 
		 * 使用sws_scale（）进行缩放/转换操作。
		 */
		sws_ctx = sws_getContext(width, height, pCodecCtx.pix_fmt(), width,height, 输出格式, SWS_BILINEAR, null, null,
				(DoublePointer) null);
	
	
		/*
		 * 分配一个适合所有内存访问的对齐内存块（包括CPU上可用的向量）。
		 */
		buffer = new BytePointer(av_malloc(numBytes));
		// 将缓冲区的适当部分分配给pFrameRGB中的图像平面
		// 请注意，pFrameRGB是一个AVFrame，但AVFrame是一个AVPicture的超集
		// avpicture_fill(new AVPicture(pFrameRGB), buffer, 图像格式, pCodecCtx.width(), pCodecCtx.height());
		AVPacket packet = new AVPacket();
		avutil.av_image_fill_arrays(pFrameRGB.data(), pFrameRGB.linesize(), buffer, 输出格式, width,
				height, 1);

		// 新建canvasFrame窗口，命名
//	        CanvasFrame canvasFrame = new CanvasFrame("测试点击");
//	        // 关闭窗口则退出程序
//	        canvasFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//		
//	        canvasFrame.setAlwaysOnTop(true);
	// canvasFrame.showImage(converter.convert(mat));
		ImageFrame iframe=new
				ImageFrame("test",width,height);
	
		int ret = -1;
		// ToMat converter = new OpenCVFrameConverter.ToMat();
		// 读视频帧一个帧
		while (av_read_frame(pFormatCtx, packet) >= 0) {
			if (packet.stream_index() == videoStream) {// 如果是图像帧
				// 解码视频帧
				// 将AVPKT大小的视频帧从AVPKT>数据解码为图片。
				// 一些解码器可以支持单个av包中的多个帧，例如解码器将只解码第一帧。
				int re = avcodec_send_packet(pCodecCtx, packet);
				if (re != 0)
					break;
				while (avcodec_receive_frame(pCodecCtx, pFrame) == 0) {
					// 读取到一帧音频或者视频
					// 处理解码后音视频 frame
					sws_scale(sws_ctx, pFrame.data(), pFrame.linesize(), 0, height, pFrameRGB.data(),
						pFrameRGB.linesize());
					
					BufferedImage img=convertimg(pFrameRGB,width,height );
					iframe.f显示(img);
					//return null;
//					if ((ret = saveImg(pFrameRGB, outfile)) >= 0) {
//						Mat mat = opencv_imgcodecs.imread(outfile);
//						break;
//					
//					}
				}
			}
		}
		// 手动释放内存
		av_packet_unref(packet);// Free the packet that was allocated by av_read_frame
		//av_free(buffer);
	    av_free(pFrameRGB);// Free the RGB image
        av_free(pFrame);// Free the YUV frame
		avcodec_free_context(pCodecCtx);
	    sws_freeContext(sws_ctx);//Free SwsContext
		// 关闭解码器
		avcodec_close(pCodecCtx);// Close the codec
		// 关闭图像源
		avformat_close_input(pFormatCtx);// Close the video file

		return "完毕";
	}



}
